package com.dries.portal.module.coupon.executor.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.dries.portal.module.coupon.enums.CouponCategory;
import com.dries.portal.module.coupon.enums.RuleFlag;
import com.dries.portal.module.coupon.executor.AbstractExecutor;
import com.dries.portal.module.coupon.executor.RuleExecutor;
import com.dries.portal.module.coupon.vo.GoodsInfo;
import com.dries.portal.module.coupon.vo.SettlementInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Description: 满减 + 折扣优惠券结算规则执行器
 * @Author ZSY
 * @createTime 2020/8/3 10:59
 */
@Slf4j
@Component
public class ManJianZheKouExecutor extends AbstractExecutor implements RuleExecutor {
    /**
     * 规则标记类型
     *
     * @return
     */
    @Override
    public RuleFlag ruleConfig() {
        return RuleFlag.MANJIAN_ZHEKOU;
    }

    /**
     * 校验商品类型与优惠券是否匹配
     * 1. 这里实现的单品类优惠券的校验，多品类优惠券重载此方法
     * 2. 如果想要使用多类优惠券，则必须要所有的商品类型都包含在内，即差集为空
     *
     * @param settlementInfo
     * @return
     */
    @Override
    protected boolean isGoodsTypeSatisfy(SettlementInfo settlementInfo) {

        log.info("检查是否是满减 + 折扣");
        // 获取商品类型
        List<Integer> goodsType = settlementInfo.getGoodsInfos().stream()
                .map(GoodsInfo::getType)
                .collect(Collectors.toList());

        List<Integer> temGoodsType = new ArrayList<>();

        settlementInfo.getCouponAndTemplateInfos().forEach(e -> {
                    temGoodsType.addAll(JSONUtil.toList(JSONUtil.parseArray(e.getTemplate().getRule().getUsage().getGoodsType()), Integer.class));
                }
        );

        // 如果想要使用多类优惠券，则必须要所有的商品类型都包含在内，即差集为空
        return CollUtil.isEmpty(CollUtil.intersection(goodsType, temGoodsType));
    }

    /**
     * 优惠券规则的计算
     *
     * @param settlement
     */
    @Override
    public SettlementInfo computeRule(SettlementInfo settlement) {

        BigDecimal goodsSum = retain2BigDecimals(goodsCostSum(settlement.getGoodsInfos()));
        SettlementInfo probability = processGoodsTypeNotSatisfy(
                settlement, goodsSum
        );

        if (probability != null) {
            log.info("优惠券与商品类型不匹配");
            return probability;
        }

        SettlementInfo.CouponAndTemplateInfo manJian = null;
        SettlementInfo.CouponAndTemplateInfo zheKou = null;

        for (SettlementInfo.CouponAndTemplateInfo info : settlement.getCouponAndTemplateInfos()) {
            if (CouponCategory.of(info.getTemplate().getCategory().getCode()) == CouponCategory.MANJIAN) {
                manJian = info;
            } else {
                zheKou = info;
            }

            assert manJian != null;
            assert zheKou != null;

            // 当前的优惠券和满减券如果不能共用,清空优惠券，返回商品原价
            if (!isTemplateCanShared(manJian,zheKou)) {
                log.info("当前商品总价不符合优惠券使用要求");
                settlement.setCost(goodsSum);
                settlement.setCouponAndTemplateInfos(Collections.emptyList());

                return settlement;
            }

            List<SettlementInfo.CouponAndTemplateInfo> ctInfos = new ArrayList<>();
            BigDecimal manJianBase = manJian.getTemplate().getRule().getDiscount().getBase();
            BigDecimal manJianQuota = manJian.getTemplate().getRule().getDiscount().getQuota();

            // 最终的价格
            BigDecimal targetSum = goodsSum;
            if (targetSum.compareTo(manJianQuota) >= 0) {
                targetSum = targetSum.subtract(manJianQuota);
                ctInfos.add(manJian);
            }
            // 再计算折扣
            BigDecimal zheKouQuota = zheKou.getTemplate().getRule().getDiscount().getQuota();
            targetSum = targetSum.multiply((zheKouQuota.multiply(BigDecimal.ONE)).divide(new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_UP));
            ctInfos.add(zheKou);

            settlement.setCouponAndTemplateInfos(ctInfos);
            // 计算使用优惠券之后的价格 - 结算
            settlement.setCost(retain2BigDecimals(
                    (targetSum.compareTo(minCostBigDecimal()) > 0
                            ? targetSum : minCostBigDecimal() )));
        }
        log.info("使用券之前需要花费的金额：{}，实际支付的金额：{}", goodsSum.toString(), settlement.getCost().toString());

        return settlement;
    }

    /**
     * 当前的两张优惠券是否可以公用
     * 即校验 TemplateRule 中的 weight 是否满足条件
     * @param manJian
     * @param zheKou
     * @return
     */
    private boolean isTemplateCanShared(SettlementInfo.CouponAndTemplateInfo manJian,
                                        SettlementInfo.CouponAndTemplateInfo zheKou) {
        String manJianKey = manJian.getTemplate().getTemplateKey() +
                String.format("%04d", manJian.getTemplate().getId());
        String zheKouKey = zheKou.getTemplate().getTemplateKey() +
                String.format("%04d", zheKou.getTemplate().getId());

        List<String> allSharedKeysForManjian = new ArrayList<>();
        allSharedKeysForManjian.add(manJianKey);
        allSharedKeysForManjian.addAll(JSONUtil.toList(
                JSONUtil.parseArray(manJian.getTemplate().getRule().getWeight()), String.class));

        List<String> allSharedKeysForZheKou = new ArrayList<>();
        allSharedKeysForZheKou.add(zheKouKey);
        allSharedKeysForZheKou.addAll(JSONUtil.toList(
                JSONUtil.parseArray(zheKou.getTemplate().getRule().getWeight()), String.class));

        return CollUtil.containsAll(Arrays.asList(manJianKey, zheKouKey), allSharedKeysForManjian)
                || CollUtil.containsAll(Arrays.asList(manJianKey, zheKouKey), allSharedKeysForZheKou);

    }

}
